/*
 ------------------------------------------------------------------------------
 Copyright (C) 2013 Eternal Games.

 This file is part of the EternalTech source code.

 The EternalTech source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The EternalTech source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the EternalTech source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// sv_send.cpp - sends packets to the client
//


#include "sv_local.h"


#define RATE_HEADER			48			// Include our header, IP header, and some overhead


/*
 ==================
 SV_WriteCommandsToClient
 ==================
*/
static void SV_WriteCommandsToClient (client_t *client, msg_t *msg){

	int		i;

	// Write any unackknowledged reliable commands
	for (i = client->reliableCommandAcknowledge + 1; i <= client->reliableCommandSequence; i++){
		MSG_WriteByte(msg, SVC_COMMAND);
		MSG_WriteLong(msg, i);
		MSG_WriteString(msg, client->reliableCommandList[i & (MAX_RELIABLE_COMMANDS - 1)]);
	}
}

/*
 ==================
 
 ==================
*/
static void SV_WriteServerStateToClient (client_t *client, msg_t *msg){

	// Don't send a server state if the client already got it or it is
	// downloading
	if (client->serverId == sv.serverId || client->downloadChecksum)
		return;

	// Don't send a server state if already sent
	if (client->serverStateMessage > client->messageAcknowledge)
		return;

	client->serverStateMessage = client->netChan.outgoingSequence;

	// Put the client in a primed state
	Com_DPrintf("Going to CS_PRIMED for %s\n", client->playerName);

	client->state = CS_PRIMED;

	client->serverId = sv.serverId;

	client->pureAuthentic = false;

	client->gameStateMessage = -1;
	client->gameStateOffset = -1;

	// Write the server state
	MSG_WriteByte(msg, SVC_SERVERSTATE);

	// Write the reliable command sequence
	MSG_WriteLong(msg, client->reliableCommandSequence);

	// Write the client number
	MSG_WriteByte(msg, client->clientNum);

	// Write the server id
	MSG_WriteLong(msg, sv.serverId);

	// TODO!!!

	// Write the pure mode
	MSG_WriteBool(msg, sv_pure->integerValue);

	// TODO!!!
}

/*
 ==================
 SV_WriteGameStateToClient
 ==================
*/
static void SV_WriteGameStateToClient (client_t *client, msg_t *msg){

	// Don't send a game state if the client isn't primed
	if (client->state != CS_PRIMED)
		return;

	// Don't send a game state if the client's pack file checksums haven't been
	// verified
	if (!client->pureAuthentic)
		return;

	// Don't send a game state if the client didn't request it
	if (client->gameStateOffset == -1)
		return;

	// Don't send a game state if already sent
	if (client->gameStateMessage > client->messageAcknowledge)
		return;

	client->gameStateMessage = client->netChan.outgoingSequence;

	// Write the game state
	MSG_WriteByte(msg, SVC_GAMESTATE);

	glqSGame->ClientWriteGameState(client->clientNum, msg, client->gameStateOffset);
}

/*
 ==================
 SV_WriteSnapshotToClient
 ==================
*/
static void SV_WriteSnapshotToClient (client_t *client, msg_t *msg){

	// Don't send a snapshot if the client isn't active
	if (client->state != CS_ACTIVE)
		return;

	// Write the snapshot
	MSG_WriteByte(msg, SVC_SNAPSHOT);

	glqSGame->ClientWriteSnapshot(client->clientNum, msg, client->netChan.outgoingSequence, client->deltaMessage, client->rateDelayed);
}

/*
 ==================
 
 ==================
*/
static void SV_WriteVoiceToClient (client_t *client, msg_t *msg){

}

/*
 ==================
 
 ==================
*/
static void SV_WriteDownloadToClient (client_t *client, msg_t *msg){

	// Don't send a download if the client didn't request it
	if (!client->downloadChecksum)
		return;
}


// ============================================================================


/*
 ==================
 SV_RateMsec

 Returns the number of milliseconds a given size packet is supposed to take to
 clear, based on the client's rate
 ==================
*/
static int SV_RateMsec (client_t *client, int packetSize){

	int		rate;

	// Individual packets will never be larger than fragment size
	if (packetSize > FRAGMENT_SIZE)
		packetSize = FRAGMENT_SIZE;

	rate = client->rate;
	if (rate > sv_maxRate->integerValue)
		rate = sv_maxRate->integerValue;

	return (packetSize + RATE_HEADER) * 1000 / rate;
}

/*
 ==================
 SV_SendPacketToClient

 Creates and sends a packet to a client.
 Also called by SV_FinalMessage.
 ==================
*/
void SV_SendPacketToClient (client_t *client, bool forceSend){

	byte	data[MAX_MSG_SIZE];
	msg_t	msg;
	int		index;
	int		rateMsec;
	int		i;

	// Make sure a previous packet is completely sent
	if (client->netChan.outgoingFragments){
		Com_DPrintf(S_COLOR_YELLOW "Sending unsent server message fragments\n");

		NetChan_TransmitAllFragments(&client->netChan, svs.time);
	}

	// Initialize the message buffer
	MSG_Init(&msg, data, sizeof(data), true);

	// Write the last reliable command we received
	MSG_WriteLong(&msg, client->clientCommandSequence);

	// Write reliable commands if needed
	SV_WriteCommandsToClient(client, &msg);

	// Write a server state if needed
	SV_WriteServerStateToClient(client, &msg);

	// Write a game state if needed
	SV_WriteGameStateToClient(client, &msg);

	// Write a snapshot if needed
	SV_WriteSnapshotToClient(client, &msg);

	// Write voice data if needed
	SV_WriteVoiceToClient(client, &msg);

	// Write a download if needed
	SV_WriteDownloadToClient(client, &msg);

	// Padding for rate debugging
	if (sv_padPackets->integerValue){
		for (i = 0; i < sv_padPackets->integerValue; i++)
			MSG_WriteByte(&msg, SVC_NOP);
	}

	// Check for overflow
	if (msg.overflowed){
		Com_Printf(S_COLOR_YELLOW "WARNING: msg overflowed for %s\n", client->playerName);

		// Clear the write state
		MSG_ClearWriteState(&msg);

		// Write the last reliable command we received
		MSG_WriteLong(&msg, client->clientCommandSequence);
	}

	// Write the EOF
	MSG_WriteByte(&msg, SVC_EOF);

	// Record information about this packet
	index = client->netChan.outgoingSequence & PACKET_MASK;

	client->packets[index].timeSent = svs.time;
	client->packets[index].timeAcked = -1;

	// Deliver the message
	NetChan_Transmit(&client->netChan, svs.time, &msg);

	if (forceSend && client->netChan.outgoingFragments)
		NetChan_TransmitAllFragments(&client->netChan, svs.time);

	// Set the next packet time based on rate and requested number of updates
	if (NET_IsLANAddress(client->netChan.remoteAddress)){
		client->nextPacketTime = svs.time;
		return;
	}

	rateMsec = SV_RateMsec(client, msg.writeBytes);

	if (client->state != CS_ACTIVE){
		client->rateDelayed = true;

		client->nextPacketTime = svs.time + rateMsec;

		// Don't pile up empty packets while connecting
		if (!client->downloadChecksum && client->nextPacketTime < svs.time + 1000)
			client->nextPacketTime = svs.time + 1000;
	}
	else {
		// Normal rateMsec / GAME_FRAMEMSEC calculation
		if (rateMsec > GAME_FRAMEMSEC){
			client->rateDelayed = true;

			client->nextPacketTime = svs.time + rateMsec;
		}
		else {
			client->rateDelayed = false;

			client->nextPacketTime = svs.time + GAME_FRAMEMSEC;
		}
	}
}

/*
 ==================
 SV_SendClientPackets
 ==================
*/
void SV_SendClientPackets (){

	client_t	*client;
	int			i;

	for (i = 0, client = svs.clients; i < sv_maxClients->integerValue; i++, client++){
		if (client->state == CS_FREE)
			continue;

		if (client->nextPacketTime > svs.time)
			continue;		// Not time yet

		// Send additional fragments if the last packet was too large to send
		// at once
		if (client->netChan.outgoingFragments){
			if (NET_IsLANAddress(client->netChan.remoteAddress))
				client->nextPacketTime = svs.time;
			else
				client->nextPacketTime = svs.time + SV_RateMsec(client, client->netChan.outgoingFragmentSize - client->netChan.outgoingFragmentOffset);

			NetChan_TransmitNextFragment(&client->netChan, svs.time);

			continue;
		}

		// Create and send a new packet to the client
		SV_SendPacketToClient(client, false);
	}
}